Imports System.Net
Imports System.Net.Sockets
Imports System.Text.Encoding
Imports System.Runtime.InteropServices

Namespace ClientSock

    Public Class ClientSocket

        Public Event onError(ByRef sDescription As String, ByRef iNumber As Integer)
        Public Event onConnect()
        Public Event onDisconnect()
        Public Event onDataRecieved(ByRef data() As Byte, ByRef bytes As Integer)

        Dim mSocket As Socket
        Dim mIP As IPAddress
        Dim mPort As Integer

        Private inBufferSize As Integer = 102400, inBuffer(102400) As Byte
        Private outBufferSize As Integer = 102400, outBuffer(102400) As Byte
        'Private outBufferSize As Integer = 20480, outBuffer(20480) As Byte
        Private outBufferDataSize As Integer = 0

        'Traffic Measurement Vars
        Public INbytes As Long, OUTbytes As Long

        Public Sub New()

            mSocket = New Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp)

        End Sub

        Public ReadOnly Property Connected As Boolean
            Get
                If Not mSocket Is Nothing Then
                    If mSocket.Connected Then
                        Return True
                    End If
                End If
                Return False
            End Get
        End Property

        Public Sub Connect(ByRef IP As IPAddress, ByRef iPort As Integer)

            If mSocket.Connected = False Then
                Try

                    mIP = IP
                    mPort = iPort

                    mSocket.BeginConnect(New IPEndPoint(IP, mPort), AddressOf SocketConnected, mSocket)

                Catch
                    RaiseEvent onError(Err.Description, Err.Number)
                End Try
            End If

        End Sub

        Public Sub Connect(ByRef hostname As String, ByRef iPort As Integer)

            Try

                Dim DNS() As IPAddress = Net.Dns.GetHostAddresses(hostname)

                If DNS.GetLength(0) > 0 Then
                    mIP = DNS(0)
                    mPort = iPort

                    Debug.Print(mIP.ToString())

                    Connect(mIP, mPort)

                Else

                    MessageBoxes.AddMessageBox("MSGBOXDNSFAIL", "Failed to lookup hostname: '" & hostname & "'!", MessageBoxes.MsgBoxButtons.OK, AddressOf Game.MsgBoxResultCallback)

                End If

            Catch

                RaiseEvent onError(Err.Description, Err.Number)

            End Try

        End Sub

        Private Sub SocketConnected(ByVal ar As IAsyncResult)
            Try
                If mSocket.Connected = False Then
                    RaiseEvent onError("Connection refused.", Err.Number)
                    Exit Sub
                End If

                RaiseEvent onConnect()
            Catch
                RaiseEvent onError(Err.Description, Err.Number)
                Exit Sub
            End Try
        End Sub

        Public Sub Disconnect()

            Try

                If Not mSocket Is Nothing Then
                    If mSocket.Connected Then
                        mSocket.Shutdown(SocketShutdown.Both)
                        'mSocket.Disconnect(True)
                        'mSocket.Disconnect(False)
                        mSocket.Close()
                        mSocket = Nothing
                    End If
                End If

            Catch
                RaiseEvent onError("(Disconnect) " & Err.Description, Err.Number)
                Exit Sub
            End Try

        End Sub

        Public Sub SendData(ByRef sData As String)

            OUTbytes += sData.Length

            Dim bytes(sData.Length) As Byte
            bytes = ASCII.GetBytes(sData)

            SendData(bytes)

        End Sub

        Public Sub SendData(ByRef sData() As Byte)

            OUTbytes += sData.Length

            sData.CopyTo(outBuffer, outBufferDataSize)
            outBufferDataSize += sData.Length

        End Sub

        Public Sub Poll()

            If mSocket Is Nothing Then
                RaiseEvent onDisconnect()
                Exit Sub
            End If

            If Not mSocket.Connected Then
                RaiseEvent onDisconnect()
                Exit Sub
            End If

            Dim SockErr As New SocketError
            Dim bytes As Integer, sent As Integer = 0
            Dim bData() As Byte = Nothing

            Try

                'Recieve Data
                If Not mSocket Is Nothing Then
                    If mSocket.Connected Then
                        If mSocket.Available > 0 Then
                            If mSocket.Poll(10, SelectMode.SelectRead) Then

                                Dim C As Integer = mSocket.Available
                                ReDim bData(C - 1)
                                bytes = mSocket.Receive(bData, 0, C, 0, SockErr)

                                If SockErr = SocketError.Success Then

                                    If bytes > 0 Then
                                        INbytes += bytes
                                    End If

                                Else

                                    bData = Nothing

                                End If

                            End If
                        End If
                    End If
                End If

            Catch

                RaiseEvent onError("(Poll) - mSocket.Poll() - " & Err.Description, Err.Number)
                Exit Sub

            End Try
            If SockErr = SocketError.Success Then
                If Not bData Is Nothing Then
                    If bytes > 0 Then
                        RaiseEvent onDataRecieved(bData, bytes)
                    End If
                End If
            End If

            'Send Data
            Try

                If outBufferDataSize > 0 Then
                    If mSocket.Connected Then
                        If mSocket.Poll(10, SelectMode.SelectWrite) Then

                            Do Until sent = outBufferDataSize

                                Try

                                    bytes = mSocket.Send(outBuffer, sent, outBufferDataSize - sent, SocketFlags.None)
                                    sent += bytes

                                Catch ex As SocketException
                                    Static tries As Integer
                                    tries += 1
                                    If tries > 5 Then
                                        RaiseEvent onError(ex.InnerException.ToString & vbCrLf & ex.Message, ex.ErrorCode)
                                        Exit Sub
                                    End If
                                    Connect(mIP, mPort)
                                End Try

                            Loop

                            outBufferDataSize = 0

                        End If
                    End If
                End If

            Catch

                RaiseEvent onError("(Poll) - mSocket.Send() - " & Err.Description, Err.Number)
                Exit Sub

            End Try

        End Sub

    End Class

End Namespace